package com.petecat.ruleengine.core.util;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import com.petecat.ruleengine.core.exception.SystemException;

public class IdWorker {
    private final long twepoch = 1288834974657L;
    private final long workerIdBits = 10L;
    private final long datacenterIdBits = 10L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final long sequenceBits = 12L;
    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;
    private ReentrantLock lock = new ReentrantLock();

   
    /**  
	 * @Title:  getWorkerId
	 * @Description:获取workerId的值
	 * @return: long
	 */
	public long getWorkerId() {
		return workerId;
	}


	/**  
	 * @Title:  getDatacenterId
	 * @Description:获取datacenterId的值
	 * @return: long
	 */
	public long getDatacenterId() {
		return datacenterId;
	}


	public IdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    public  long nextId() {
    	try {
			if(lock.tryLock(30000,TimeUnit.MILLISECONDS)) {
			    try {
					long timestamp = timeGen();
					if (timestamp < lastTimestamp) {
					    throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
					}
					if (lastTimestamp == timestamp) {
					    sequence = (sequence + 1) & sequenceMask;
					    if (sequence == 0) {
					        timestamp = tilNextMillis(lastTimestamp);
					    }
					} else {
					    sequence = 0L;
					}

					lastTimestamp = timestamp;

					return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
				} finally {
					lock.unlock();
				}
			}else {
				throw new SystemException(9011,null,null);
			}
		} catch (Exception e) {
			  throw new SystemException(9011,null,null);
		}
    }

    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    protected long timeGen() {
        return System.currentTimeMillis();
    }
}
